import akshare as ak
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta, date
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
import threading
import re
from tkcalendar import Calendar, DateEntry
from dateutil.relativedelta import relativedelta
import requests
from bs4 import BeautifulSoup
import os
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
import traceback
from matplotlib.dates import DateFormatter


# 设置中文字体和后端
matplotlib.use("TkAgg")
plt.rcParams["font.family"] = ["SimHei"]  # 仅保留存在的字体
plt.rcParams["axes.unicode_minus"] = False

class AShareTradingDayChecker:
    """中国A股交易日检查工具"""
    def __init__(self):
        self.cache = {}
        self.holiday_api = "http://tool.bitefu.net/jiari/"
    
    def is_trading_day(self, date: date) -> bool:
        """判断指定日期是否为A股交易日（排除周末和节假日）"""
        if date.weekday() >= 5:  # 排除周末
            return False
        return self._is_working_day(date)
    
    def _is_working_day(self, date: date) -> bool:
        """检查是否为工作日（使用API或备用规则）"""
        date_str = date.strftime("%Y%m%d")
        
        # 优先使用缓存
        if date_str in self.cache:
            return self.cache[date_str]
        
        try:
            # 请求节假日API
            response = requests.get(self.holiday_api, params={"d": date_str})
            if response.status_code == 200:
                result = int(response.text)
                # 0: 工作日, 1: 休息日, 2: 节假日（均非交易日）
                is_working = result == 0
                self.cache[date_str] = is_working
                return is_working
        except Exception as e:
            print(f"节假日API请求失败: {e}")
        
        # API不可用时的备用规则（覆盖常见节假日，需根据实际情况更新）
        return self._fallback_is_working_day(date)
    
    def _fallback_is_working_day(self, date: date) -> bool:
        """备用节假日判断（仅示例，需根据最新放假安排调整）"""
        # 定义常见节假日（月, 日）
        holidays = [
            (1, 1),   # 元旦
            (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7),  # 春节
            (4, 5),   # 清明
            (5, 1),   # 劳动节
            (6, 10), (6, 11), (6, 12),  # 端午（2025年示例，需动态调整）
            (9, 29), (9, 30), (10, 1), (10, 2), (10, 3), (10, 4), (10, 5)  # 中秋国庆（2025年示例）
        ]
        month_day = (date.month, date.day)
        return month_day not in holidays
    
    def get_trading_days(self, start_date: date, end_date: date) -> list[date]:
        """获取指定区间内的所有交易日"""
        trading_days = []
        current_date = start_date
        while current_date <= end_date:
            if self.is_trading_day(current_date):
                trading_days.append(current_date)
            current_date += relativedelta(days=1)
        return trading_days
    

class DeliveryDateCalculator:
    """期货期权交割日计算器"""
    def __init__(self, trading_day_checker):
        self.trading_day_checker = trading_day_checker
    
    def get_monthly_delivery_dates(self, start_date, end_date):
        """获取指定范围内的月度交割日（第三个星期五）"""
        dates = []
        current_date = start_date.replace(day=1)
        
        while current_date <= end_date:
            # 找当月的第三个星期五
            month = current_date.month
            candidate = datetime(current_date.year, month, 1)
            
            friday_count = 0
            while candidate.month == month:
                if candidate.weekday() == 4:  # 星期五
                    friday_count += 1
                    if friday_count == 3:
                        # 检查是否为交易日
                        delivery_date = self._adjust_to_trading_day(candidate.date())
                        if start_date <= delivery_date <= end_date:
                            dates.append(delivery_date)
                        break
                candidate += timedelta(days=1)
            
            current_date += relativedelta(months=1)
        
        return dates
    
    def _adjust_to_trading_day(self, date):
        """调整到最近的交易日"""
        for delta in [0, 1, -1, 2, -2, 3, -3]:
            new_date = date + timedelta(days=delta)
            if self.trading_day_checker.is_trading_day(new_date):
                return new_date
        return date

class DatePicker(ttk.Frame):
    """日期选择器组件"""
    def __init__(self, parent, default_date=None):
        super().__init__(parent)
        self.parent = parent
        
        # 如果没有提供默认日期，使用今天的日期
        print("传入的默认日期:", default_date)
        if default_date:
            print("进入 if 分支")
            # 确保 default_date 是 datetime 对象
            if isinstance(default_date, datetime):
                date_str = default_date.strftime('%Y-%m-%d')
            else:
                date_str = datetime.strptime(str(default_date), '%Y-%m-%d').strftime('%Y-%m-%d')
        else:
            date_str = datetime.now().strftime('%Y-%m-%d')
        
        print("value=",date_str)
        # 正确打印 date_var 的值
        self.date_var = tk.StringVar(value=date_str)
        print("date_var 的值:", self.date_var.get())
        default_date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()

        # 创建日期输入框
        try:
            self.date_entry = DateEntry(
                self, 
                textvariable=self.date_var, 
                locale='zh_CN',  # 设置中文显示
                width=12, 
                date=default_date_obj,  # ? 显式指定默认日期
                date_pattern='YYYY-MM-DD',  # 使用大写格式符
                background='darkblue',
                foreground='white',
                borderwidth=2
            )
        except ValueError:
            print("error here")
            return None
        self.date_entry.pack(side=tk.LEFT, padx=(0, 5))
        # ? 手动恢复 textvariable 的值（防止控件修改）
        self.date_var.set(date_str)
        print("date_var after 的值:", self.date_var.get())
        # 创建日期选择按钮
        self.date_btn = ttk.Button(self, text="日期选择", command=self.select_date)
        self.date_btn.pack(side=tk.LEFT)
    
    def get_date(self):
        """获取选择的日期"""
        try:
            return datetime.strptime(self.date_var.get(), '%Y-%m-%d')
        except ValueError:
            messagebox.showerror("日期格式错误", "请使用YYYY-MM-DD格式的日期")
            return None
    
    def select_date(self):
        """打开日期选择对话框"""
        def on_date_selected():
            selected_date = cal.selection_get()
            self.date_var.set(selected_date.strftime('%Y-%m-%d'))
            top.destroy()
        
        top = tk.Toplevel(self)
        top.title("选择日期")
        top.geometry("400x350")
        
        # 创建日历控件
        cal = Calendar(
            top,
            font="Arial 10",
            locale='zh_CN',  # 设置中文显示
            selectmode='day',
            cursor="hand1",
            year=self.get_date().year,
            month=self.get_date().month,
            day=self.get_date().day
        )
        cal.pack(fill="both", expand=True, padx=10, pady=10)
        
        # 创建确认按钮
        ttk.Button(top, text="确认", command=on_date_selected).pack(pady=10)

class Logger:
    def __init__(self, status_var=None):
        self.status_var = status_var
        self.logs = []
    
    def log(self, message):
        """记录日志并更新状态栏"""
        print(message)
        self.logs.append(message)
        if self.status_var:
            self.status_var.set(message)
    
    def get_logs(self):
        return "\n".join(self.logs)
    
class StockAnalysisApp:
    def __init__(self, root):
        self.root = root
        self.root.title("A股资金流向与指数/成交额分析")
        self.root.geometry("1200x800")
        self.root.minsize(1000, 700)
        
        # 数据缓存
        self.current_data = None
        self.turnover_data = None
        self.main_data = None
        self.north_data = None
        self.turnover_cache = {}
        self.cache_days_limit = 3650
        self.delivery_analysis_cache = {}

        # 添加成交额统计变量
        self.max_turnover = tk.StringVar()
        self.min_turnover = tk.StringVar()
        self.avg_turnover = tk.StringVar()
        self.total_turnover = tk.StringVar()
        
        # 初始化默认值
        self.max_turnover.set("--")
        self.min_turnover.set("--")
        self.avg_turnover.set("--")
        self.total_turnover.set("--")

        self.max_inflow = tk.StringVar(value="最大净流入: --")
        self.max_outflow = tk.StringVar(value="最大净流出: --")
        self.avg_inflow = tk.StringVar(value="平均净流入: --")
        self.total_inflow = tk.StringVar(value="总净流入: --")
        
        self.max_turnover = tk.StringVar(value="最大成交额: --")
        self.min_turnover = tk.StringVar(value="最小成交额: --")
        self.avg_turnover = tk.StringVar(value="平均成交额: --")
        self.total_turnover = tk.StringVar(value="总成交额: --")
        
        # 控制变量
        self.north_enabled = tk.BooleanVar(value=False)
        self.trading_day_checker = AShareTradingDayChecker()
        self.delivery_calculator = DeliveryDateCalculator(self.trading_day_checker)

        self.index_update_lock = False  # 防止重复更新
        self.last_index_params = None   # 缓存最后使用的指数参数
        
        # 自动更新相关
        self.update_job = None
        self.last_update_params = None

        # 第一步：初始化日志系统
        self.logger = Logger()  # 先初始化logger
        
        # 第二步：检查akshare版本（需要logger已存在）
        self.check_akshare_version()

        self.index_update_job = None  # 指数更新任务
        self.fund_update_job = None   # 新增：资金流向更新任务
        self.turnover_update_job = None  # 成交额更新任务

        self.turnover_analysis_running = False  # 成交额分析状态
        self.fund_analysis_running = False
        
        # 初始化界面
        self.create_widgets()
        self.create_overview_cards()  # 确保调用
        self.setup_autoupdate()
        self.check_akshare_version()
        
        # 文件缓存
        self.cache_dir = "stock_cache"
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir)
            
        # 初始加载
        self.root.after(100, self.initial_load)


    def schedule_turnover_update(self, *args):
        """成交额更新调度（防抖处理）"""
        if self.turnover_update_job:
            self.root.after_cancel(self.turnover_update_job)
        self.turnover_update_job = self.root.after(800, self.trigger_turnover_update)

    def trigger_turnover_update(self):
        """触发成交额更新"""
        try:
            self.logger.log("触发成交额更新检查")
            
            if not self.validate_dates(show_error=False):
                self.logger.log("日期验证失败，取消成交额更新")
                return
            
            current_params = (
                self.start_date_picker.get_date().strftime("%Y%m%d"),
                self.end_date_picker.get_date().strftime("%Y%m%d")
            )
            
            if hasattr(self, 'last_turnover_params') and current_params == self.last_turnover_params:
                self.logger.log("成交额参数未变化，取消更新")
                return
                
            self.last_turnover_params = current_params
            self.logger.log(f"开始成交额分析，参数：{current_params}")
            self.start_turnover_analysis()
            
        except Exception as e:
            self.logger.log(f"触发成交额更新失败: {str(e)}")
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"成交额更新失败: {msg}"))


    def start_turnover_analysis(self):
        """启动成交额分析"""
        try:
            if self.turnover_analysis_running:
                self.logger.log("已有成交额分析在进行中，取消本次请求")
                return
                
            self.turnover_analysis_running = True
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            
            analysis_thread = threading.Thread(
                target=self.perform_turnover_analysis,
                args=(start_date.date(), end_date.date()),  # 确保传入date对象
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"启动失败: {msg}"))

    def perform_turnover_analysis(self, start_date, end_date):
        """执行成交额分析"""
        try:
            self.logger.log(f"获取成交额数据 {start_date} 至 {end_date}")
            
            # 获取成交额数据（带缓存机制）
            turnover_data = self.get_market_turnover(start_date, end_date)
            
            # 主线程更新
            self.root.after(0, lambda: self._update_turnover_ui(turnover_data))
            
        except Exception as e:
            error_msg = f"成交额分析失败: {str(e)}"
            self.root.after(0, lambda msg=error_msg: self.status_var.set(msg))
            self.logger.log(error_msg)
        finally:
            self.turnover_analysis_running = False

    def _update_turnover_ui(self, turnover_data):
        """更新成交额界面（添加验证）"""
        try:
            if turnover_data is None or turnover_data.empty:
                raise ValueError("成交额数据为空")
                
            # 验证必须字段
            required_columns = {'date', 'total_turnover'}
            if not required_columns.issubset(turnover_data.columns):
                missing = required_columns - set(turnover_data.columns)
                raise ValueError(f"成交额数据缺失必要列: {missing}")
                
            # 合并到现有数据
            if self.current_data is not None:
                # 确保现有数据有date列
                if 'date' not in self.current_data.columns:
                    raise ValueError("当前数据缺失date列")
                    
                merged = pd.merge(
                    self.current_data,
                    turnover_data[['date', 'total_turnover']],
                    on='date',
                    how='left'
                )
            else:
                merged = turnover_data
                
            # 检查合并结果
            if 'date' not in merged.columns:
                raise ValueError("合并后数据缺失date列")
                
            # 更新图表
            self.plot_turnover_trend(merged)
            
            # 更新统计信息
            self._update_turnover_stats(merged)
            
            self.status_var.set(f"成交额已更新（{len(merged)}个交易日）")
            
        except Exception as e:
            self.logger.log(f"成交额界面更新失败: {str(e)}")
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"错误: {msg}"))

    def _update_turnover_stats(self, data):
        """更新成交额统计信息（健壮版）"""
        try:
            if not hasattr(self, 'max_turnover'):
                raise AttributeError("统计变量未初始化")
                
            valid_data = data[data['total_turnover'].notna()]
            
            if not valid_data.empty:
                stats = {
                    'max': valid_data['total_turnover'].max(),
                    'min': valid_data['total_turnover'].min(),
                    'mean': valid_data['total_turnover'].mean(),
                    'total': valid_data['total_turnover'].sum()
                }
                
                # 使用条件判断确保变量存在
                if hasattr(self, 'max_turnover'):
                    self.max_turnover.set(f"最大成交额: {stats['max']:.2f} 亿元")
                if hasattr(self, 'min_turnover'):    
                    self.min_turnover.set(f"最小成交额: {stats['min']:.2f} 亿元")
                if hasattr(self, 'avg_turnover'):
                    self.avg_turnover.set(f"平均成交额: {stats['mean']:.2f} 亿元")
                if hasattr(self, 'total_turnover'):
                    self.total_turnover.set(f"总成交额: {stats['total']/10000:.2f} 万亿元")
                    
            else:
                self.max_turnover.set("--")
                self.min_turnover.set("--")
                self.avg_turnover.set("--")
                self.total_turnover.set("--")
                
        except Exception as e:
            self.logger.log(f"统计更新失败: {str(e)}")

    def _clean_turnover_cache(self):
        """清理过期缓存（保留最近30天）"""
        cutoff_date = datetime.now().date() - timedelta(days=30)
        keys_to_delete = []
        
        for (start, end), data in self.turnover_cache.items():
            if end < cutoff_date:
                keys_to_delete.append((start, end))
        
        for key in keys_to_delete:
            del self.turnover_cache[key]

    def schedule_fund_update(self, *args):
        """资金流向更新调度（防抖处理）"""
        if self.fund_update_job:
            self.root.after_cancel(self.fund_update_job)
        self.fund_update_job = self.root.after(800, self.trigger_fund_update)

    def trigger_fund_update(self):
        """触发资金流向更新"""
        try:
            # 添加调试日志
            self.logger.log("触发资金流向更新检查")
            
            if not self.validate_dates(show_error=False):
                self.logger.log("日期验证失败，取消更新")
                return
            
            current_params = (
                self.start_date_picker.get_date().strftime("%Y%m%d"),
                self.end_date_picker.get_date().strftime("%Y%m%d"),
                self.north_enabled.get()
            )
            
            # 检查参数是否有变化
            if hasattr(self, 'last_fund_params') and current_params == self.last_fund_params:
                self.logger.log("资金流向参数未变化，取消更新")
                return
                
            self.last_fund_params = current_params
            self.logger.log(f"开始资金流向分析，参数：{current_params}")
            self.start_fund_analysis()
            
        except Exception as e:
            self.logger.log(f"触发资金流向更新失败: {str(e)}")
            messagebox.showerror("更新错误", f"资金流向更新失败: {str(e)}")


    def start_fund_analysis(self):
        """启动资金流向分析"""
        try:
            print("start_fund_analysis in1")
            if self.fund_analysis_running:
                self.logger.log("已有资金分析在进行中，取消本次请求")
                return
            print("start_fund_analysis in1")    
            self.fund_analysis_running = True
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            print("start_fund_analysis in")
            analysis_thread = threading.Thread(
                target=self.perform_fund_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            # 修复点：通过默认参数捕获当前异常值
            self.root.after(0, lambda e=e: self.status_var.set(f"启动失败: {str(e)}"))

    def perform_fund_analysis(self, start_date, end_date):
        """执行资金分析（带异常处理）"""
        try:
            # 在后台线程获取数据
            main_data = self.get_main_money_flow_data(start_date, end_date)
            north_data = self.get_north_flow_data(start_date, end_date) if self.north_enabled.get() else None
            print("perform_fund_analysis in")
            # 回到主线程更新
            self.root.after(0, lambda: self._update_fund_ui(main_data, north_data))
            
        except Exception as e:
            error_msg = f"资金分析失败: {str(e)}"
            #self.root.after(0, lambda: self.status_var.set(error_msg))
            self.root.after(0, lambda msg=error_msg: self.status_var.set(msg))
            self.logger.log(error_msg)
        finally:
            self.fund_analysis_running = False

    
    def _update_fund_ui(self, main_data, north_data):
        """主线程更新UI"""
        print("_update_fund_ui in")
        try:
            # 合并数据
            merged = self.merge_data(
                self.current_data,  # 使用已有的指数数据
                north_data,
                main_data,
                self.north_enabled.get(),
                main_data is not None,
                self.turnover_data
            )
            
            # 更新图表
            self.plot_fund_flow_trend(merged, self.north_enabled.get(), True)
            self.plot_correlation_scatter(merged, self.north_enabled.get(), True)
            
            # 更新统计数据
            correlations = self.calculate_correlations(merged, self.north_enabled.get(), True)
            self.update_overview_cards(correlations, self.north_enabled.get(), True)
            
            self.status_var.set(f"资金流向已更新（{len(merged)}个交易日）")
        except Exception as e:
            messagebox.showerror("更新错误", f"界面更新失败: {str(e)}")

    def update_fund_display(self, merged_data):
        """更新资金流向显示"""
        try:
            # 更新资金流向趋势图
            self.plot_fund_flow_trend(merged_data, 
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            
            # 更新相关性散点图
            self.plot_correlation_scatter(merged_data,
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            
            # 更新统计信息
            correlations = self.calculate_correlations(merged_data,
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            self.update_overview_cards(correlations,
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            
            self.status_var.set(f"主力资金数据更新完成（{len(merged_data)}个交易日）")
        except Exception as e:
            self.logger.log(f"资金流向显示更新失败: {str(e)}")

    def analyze_delivery_impact(self):
        """分析交割日对指数的影响"""
        self.logger.log("开始分析交割日影响...")
        if self.current_data is None or self.current_data.empty:
            messagebox.showinfo("提示", "请先完成基础分析")
            return
        
        try:
            # 获取分析参数
            days_before = 3
            days_after = 3
            
            # 获取交割日数据
            start_date = self.current_data['date'].min().date()
            end_date = self.current_data['date'].max().date()
            delivery_dates = self.delivery_calculator.get_monthly_delivery_dates(start_date, end_date)
            
            # 检查缓存
            cache_key = (start_date, end_date, days_before, days_after)
            if cache_key in self.delivery_analysis_cache:
                result = self.delivery_analysis_cache[cache_key]
            else:
                # 准备分析数据
                analysis_data = []

                self.logger.log(f"找到{len(delivery_dates)}个交割日")
                
                for delivery_date in delivery_dates:
                    # 获取交割日前后数据
                    start_window = delivery_date - timedelta(days=days_before)
                    end_window = min(delivery_date + timedelta(days=days_after), self.current_data['date'].max().date())
                    
                    # 转换日期格式确保类型匹配
                    window_data = self.current_data[
                        (self.current_data['date'] >= pd.to_datetime(start_window)) &
                        (self.current_data['date'] <= pd.to_datetime(end_window))
                    ].copy()
                    
                    if not window_data.empty:
                        # 计算相对交割日的天数
                        window_data['day_offset'] = (window_data['date'].dt.date - delivery_date).apply(lambda x: x.days)
                        # 过滤有效偏移范围（-3到+3天）
                        window_data = window_data[(window_data['day_offset'] >= -days_before) & 
                                                 (window_data['day_offset'] <= days_after)]
                        
                        analysis_data.append(window_data)
                        self.logger.log(f"交割日{delivery_date}找到{len(window_data)}条数据")
                    else:
                        self.logger.log(f"警告：交割日{delivery_date}附近无数据")
                        analysis_data.append(window_data)
                
                if not analysis_data:
                    messagebox.showinfo("提示", "当前数据范围内无交割日")
                    return
                
                # 合并数据
                combined = pd.concat(analysis_data)
                self.logger.log(f"合并后总数据量：{len(combined)}条")
                
                # 分组计算统计量
                grouped = combined.groupby('day_offset')['pct_change'].agg(
                    mean_change='mean',
                    std_change='std',
                    count='count'
                ).reset_index()
                
                # 计算成交额变化
                turnover_grouped = combined.groupby('day_offset')['total_turnover'].agg(
                    mean_turnover='mean',
                    std_turnover='std'
                ).reset_index()
                
                result = {
                    'grouped': grouped,
                    'turnover_grouped': turnover_grouped,
                    'delivery_dates': delivery_dates
                }
                self.delivery_analysis_cache[cache_key] = result
            
            # 更新图表
            self.plot_delivery_impact(result)
            
            # 显示统计信息
            self.show_delivery_stats(result)
           
        except Exception as e:
            messagebox.showerror("分析失败", str(e))
            self.logger.log(f"交割日影响分析失败: {e}")

    def plot_delivery_impact(self, result):
        """绘制交割日影响图表"""
        self.delivery_ax1.clear()
        self.delivery_ax2.clear()

        if result['grouped'].empty or result['turnover_grouped'].empty:
            messagebox.showinfo("提示", "无有效数据可供展示")
            return
        
        
        # 绘制涨跌幅变化
        self.delivery_ax1.errorbar(
            result['grouped']['day_offset'],
            result['grouped']['mean_change'],
            yerr=result['grouped']['std_change'],
            fmt='-o',
            capsize=5,
            label='平均涨跌幅'
        )
        self.delivery_ax1.axvline(0, color='red', linestyle='--', alpha=0.5)
        self.delivery_ax1.set_title("交割日前后指数涨跌幅变化（{}个周期）".format(len(result['delivery_dates'])))
        self.delivery_ax1.set_xlabel("相对交割日天数")
        self.delivery_ax1.set_ylabel("涨跌幅 (%)")
        self.delivery_ax1.grid(True)
        self.delivery_ax1.legend()
        
        # 绘制成交额变化
        self.delivery_ax2.bar(
            result['turnover_grouped']['day_offset'],
            result['turnover_grouped']['mean_turnover'],
            yerr=result['turnover_grouped']['std_turnover'],
            capsize=5,
            color='orange',
            alpha=0.7,
            label='平均成交额'
        )
        self.delivery_ax2.axvline(0, color='red', linestyle='--', alpha=0.5)
        self.delivery_ax2.set_title("交割日前后成交额变化")
        self.delivery_ax2.set_xlabel("相对交割日天数")
        self.delivery_ax2.set_ylabel("成交额 (亿元)")
        self.delivery_ax2.grid(True)
        self.delivery_ax2.legend()
        
        self.delivery_fig.tight_layout()
        self.delivery_canvas.draw()

    def show_delivery_stats(self, result):
        """显示交割日统计信息"""
        stats = []

        stats.append(f"分析周期：{result['grouped']['day_offset'].min()} 至 {result['grouped']['day_offset'].max()} 天")
        stats.append(f"包含交割日数量：{len(result['delivery_dates'])}个")
        
        # 计算关键指标
        delivery_day = result['grouped'][result['grouped']['day_offset'] == 0]
        if not delivery_day.empty:
            stats.append(f"? 交割日平均涨跌幅: {delivery_day['mean_change'].values[0]:.2f}%")
            stats.append(f"? 交割日波动率: {delivery_day['std_change'].values[0]:.2f}%")
        
        before_days = result['grouped'][result['grouped']['day_offset'] < 0]
        after_days = result['grouped'][result['grouped']['day_offset'] > 0]
        
        if not before_days.empty:
            stats.append(f"? 交割日前平均涨跌幅: {before_days['mean_change'].mean():.2f}%")
        if not after_days.empty:
            stats.append(f"? 交割日后平均涨跌幅: {after_days['mean_change'].mean():.2f}%")

                
        # 添加成交额统计
        stats.append(f"交割日平均成交额: {result['turnover_grouped'].loc[result['turnover_grouped']['day_offset'] == 0, 'mean_turnover'].values[0]:.2f}亿")
        
        # 在交割日选项卡中添加统计信息显示
        for widget in self.delivery_frame.winfo_children():
            if isinstance(widget, ttk.Label):
                widget.destroy()
        
        stats_text = "\n".join(stats)
        stats_label = ttk.Label(self.delivery_frame, text=stats_text)
        stats_label.pack(pady=10)
    
    def check_akshare_version(self):
        """检查akshare版本并显示提示"""
        try:
            version = ak.__version__
            self.logger.log(f"已加载 akshare v{version}")
            # 检查是否为较新版本
            major, minor, _ = map(int, re.findall(r'\d+', version)[:3])
            if major < 1 or (major == 1 and minor < 30):
                messagebox.showinfo("版本提示", 
                    f"您的akshare版本为{version}，建议升级到最新版本以获取最佳兼容性。\n"
                    "可通过命令: 'pip install akshare --upgrade' 进行升级。")
        except Exception as e:
            self.logger.log(f"检查akshare版本时出错: {e}")
            messagebox.showerror("版本检查失败", f"无法确定akshare版本，请确保已安装最新版本")
    
    def create_widgets(self):
        """完整界面创建代码"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 概览卡片
        self.overview_frame = ttk.LabelFrame(main_frame, text="数据分析概览", padding="10")
        self.overview_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 控制面板
        self.control_frame = ttk.LabelFrame(main_frame, text="分析参数设置", padding="10")
        self.control_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 指数选择
        ttk.Label(self.control_frame, text="选择指数:").grid(row=0, column=0, sticky=tk.W)
        self.index_var = tk.StringVar(value="sh000001")
        index_frame = ttk.Frame(self.control_frame)
        index_frame.grid(row=0, column=1, sticky=tk.W)
        
        index_options = [
            ("上证指数", "sh000001"),
            ("深证成指", "sz399001"),
            ("创业板指", "sz399006"),
            ("科创50", "sh000688"),
            ("沪深300", "sh000300")
        ]
        
        # 动态生成单选按钮
        for i, (text, value) in enumerate(index_options):
            row = i // 3
            col = i % 3
            ttk.Radiobutton(index_frame, text=text, variable=self.index_var, 
                          value=value).grid(row=row, column=col, padx=10, pady=5)
        
        # 日期选择
        end_date_default = datetime.now()
        print(end_date_default)
        start_date_default = end_date_default - relativedelta(months=6)
        print(start_date_default)
        #self.start_date_picker = DatePicker(self.control_frame, start_date_default)
        #self.end_date_picker = DatePicker(self.control_frame, end_date_default)


        ttk.Label(self.control_frame, text="开始日期:").grid(row=1, column=0, sticky=tk.W)
        self.start_date_picker = DatePicker(self.control_frame, start_date_default)
        #self.start_date_picker = DatePicker(self.control_frame, datetime.now() - timedelta(days=30))
        self.start_date_picker.grid(row=1, column=1, sticky=tk.W)
        
        ttk.Label(self.control_frame, text="结束日期:").grid(row=2, column=0, sticky=tk.W)
        #self.end_date_picker = DatePicker(self.control_frame, datetime.now())
        self.end_date_picker = DatePicker(self.control_frame, end_date_default)
        self.end_date_picker.grid(row=2, column=1, sticky=tk.W)



        
        # 资金类型选择
        ttk.Label(self.control_frame, text="分析选项:").grid(row=3, column=0, sticky=tk.W)
        option_frame = ttk.Frame(self.control_frame)
        option_frame.grid(row=3, column=1, sticky=tk.W)
        ttk.Checkbutton(option_frame, text="包含北向资金", variable=self.north_enabled).pack(side=tk.LEFT)
        
        # 状态提示
        ttk.Label(self.control_frame, text="※ 修改参数自动刷新", 
                foreground="#666", font=("SimHei", 9)).grid(
                    row=0, column=2, rowspan=4, 
                    padx=20, pady=5, sticky=tk.NSEW)
        
        # 结果区域
        self.notebook = ttk.Notebook(main_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True)
        
        # 各选项卡初始化（同原有实现）
        self.init_index_tab()        # 指数趋势
        self.init_fund_flow_tab()    # 资金流向趋势（修正方法名）
        self.init_turnover_tab()     # 成交额趋势
        self.init_scatter_tab()      # 相关性分析
        self.init_data_tab()         # 详细数据
        self.init_delivery_tab()     # 交割日影响
        #self.init_stats_tab()        # 统计信息
        
        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        ttk.Label(main_frame, textvariable=self.status_var, 
                relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X)
        
    def init_index_tab(self):
        """初始化指数趋势选项卡"""
        frame = ttk.Frame(self.notebook)
        self.notebook.add(frame, text="指数趋势")
        
        # 创建指数趋势图
        self.index_fig = Figure(figsize=(10, 5), dpi=100)
        self.index_ax = self.index_fig.add_subplot(111)
        self.index_canvas = FigureCanvasTkAgg(self.index_fig, master=frame)
        self.index_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 添加工具栏
        toolbar = NavigationToolbar2Tk(self.index_canvas, frame)
        toolbar.update()

    def init_fund_flow_tab(self):  # 修正方法名
        """初始化资金流向趋势选项卡"""
        self.fund_flow_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.fund_flow_frame, text="资金流向趋势")
        
        # 创建资金流向趋势图
        self.fund_flow_fig = Figure(figsize=(10, 5), dpi=100)
        self.fund_flow_ax = self.fund_flow_fig.add_subplot(111)
        self.fund_flow_canvas = FigureCanvasTkAgg(self.fund_flow_fig, master=self.fund_flow_frame)
        self.fund_flow_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=10)

    def init_turnover_tab(self):
        """初始化成交额趋势选项卡"""
        self.turnover_trend_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.turnover_trend_frame, text="成交额趋势")
        
        # 创建成交额趋势图
        self.turnover_fig = Figure(figsize=(10, 5), dpi=100)
        self.turnover_ax = self.turnover_fig.add_subplot(111)
        self.turnover_canvas = FigureCanvasTkAgg(self.turnover_fig, master=self.turnover_trend_frame)
        self.turnover_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=10)

    def init_fund_flow_tab(self):
        """初始化资金流向趋势选项卡（修改后）"""
        self.fund_flow_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.fund_flow_frame, text="资金流向趋势")
        
        # 添加控制按钮
        control_frame = ttk.Frame(self.fund_flow_frame)
        control_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(control_frame, text="立即刷新", 
                command=self.trigger_fund_update).pack(side=tk.RIGHT, padx=5)
        
        # 创建图表
        self.fund_flow_fig = Figure(figsize=(10, 5), dpi=100)
        self.fund_flow_ax = self.fund_flow_fig.add_subplot(111)
        self.fund_flow_canvas = FigureCanvasTkAgg(self.fund_flow_fig, master=self.fund_flow_frame)
        self.fund_flow_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 添加工具栏
        toolbar = NavigationToolbar2Tk(self.fund_flow_canvas, self.fund_flow_frame)
        toolbar.update()

    def init_scatter_tab(self):
        """初始化相关性分析选项卡"""
        self.scatter_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.scatter_frame, text="相关性分析")
        
        # 创建散点图
        self.scatter_fig = Figure(figsize=(10, 5), dpi=100)
        self.scatter_ax = self.scatter_fig.add_subplot(111)
        self.scatter_canvas = FigureCanvasTkAgg(self.scatter_fig, master=self.scatter_frame)
        self.scatter_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def init_data_tab(self):
        """初始化详细数据选项卡"""
        self.data_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.data_frame, text="详细数据")
        
        # 创建数据表格
        self.data_tree = ttk.Treeview(self.data_frame)
        scrollbar = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_tree.yview)
        self.data_tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.data_tree.pack(fill=tk.BOTH, expand=True)
        
        # 导出按钮
        self.export_btn = ttk.Button(self.data_frame, text="导出数据", command=self.export_data)
        self.export_btn.pack(side=tk.RIGHT, padx=10, pady=10)

    def init_delivery_tab(self):
        """初始化交割日影响选项卡"""
        self.delivery_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.delivery_frame, text="交割日影响")
        
        # 创建交割日分析图表
        self.delivery_fig = Figure(figsize=(10, 6), dpi=100)
        self.delivery_ax1 = self.delivery_fig.add_subplot(211)
        self.delivery_ax2 = self.delivery_fig.add_subplot(212)
        self.delivery_canvas = FigureCanvasTkAgg(self.delivery_fig, master=self.delivery_frame)
        self.delivery_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        ttk.Button(self.delivery_frame, text="分析交割日影响", command=self.analyze_delivery_impact).pack(pady=5)

    def setup_autoupdate(self):
        """设置自动更新绑定（修改后）"""
        # 仅绑定指数相关参数的变化
        self.index_var.trace_add('write', self.schedule_index_update)
        self.start_date_picker.date_var.trace_add('write', self.schedule_index_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_index_update)

        # 资金流向相关
        self.start_date_picker.date_var.trace_add('write', self.schedule_fund_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_fund_update)
        self.north_enabled.trace_add('write', self.schedule_fund_update)  # 确保北向资金开关绑定

        # 新增成交额绑定
        self.start_date_picker.date_var.trace_add('write', self.schedule_turnover_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_turnover_update)
    
        # 确保指数更新也触发成交额更新
        self.index_var.trace_add('write', lambda *_: self.schedule_turnover_update())

    def schedule_index_update(self, *args):
        """指数更新调度（新增方法）"""
        if self.update_job:
            self.root.after_cancel(self.update_job)
        self.update_job = self.root.after(800, self.trigger_index_update)

    def trigger_index_update(self):
        """触发指数更新（新增方法）"""
        if not self.validate_dates(show_error=False) or self.index_update_lock:
            return

        current_params = (
            self.index_var.get(),
            self.start_date_picker.get_date().strftime("%Y%m%d"),
            self.end_date_picker.get_date().strftime("%Y%m%d")
        )
        
        if current_params == self.last_index_params:
            return
            
        self.last_index_params = current_params
        self.start_index_analysis()

    def start_index_analysis(self):
        """启动指数分析（新增方法）"""
        try:
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            
            analysis_thread = threading.Thread(
                target=self.perform_index_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.status_var.set(f"指数更新失败: {str(e)}")

    def perform_index_analysis(self, start_date, end_date):
        """执行指数分析（新增方法）"""
        try:
            self.root.after(0, lambda: self.status_var.set("正在加载指数数据..."))
            
            # 获取指数数据
            index_code = self.index_var.get()
            index_data = self.get_market_index_data(index_code, start_date, end_date)
            
            if index_data is None or index_data.empty:
                raise Exception("获取指数数据失败")

            # 更新图表和数据
            self.root.after(0, lambda: self.update_index_display(index_data))
            
        except Exception as e:
            self.root.after(0, lambda: self.status_var.set(f"指数分析失败: {str(e)}"))
            self.logger.log(f"指数分析错误: {traceback.format_exc()}")

    def update_index_display(self, index_data):
        """更新指数显示（新增方法）"""
        try:
            # 更新指数趋势图
            self.plot_index_trend(index_data, self.index_var.get())
            
            # 更新数据表格（仅显示指数数据）
            self.current_data = index_data[['date', 'close', 'pct_change']]
            self.update_data_table(self.current_data)
            
            # 更新状态
            self.status_var.set(f"指数数据更新完成（{len(index_data)}个交易日）")
            
            # 清除其他分析的缓存
            self.turnover_cache.clear()
            self.delivery_analysis_cache.clear()
        except Exception as e:
            self.logger.log(f"指数显示更新失败: {str(e)}")

    def schedule_update(self, *args):
        """调度更新（带500ms防抖）"""
        if self.update_job:
            self.root.after_cancel(self.update_job)
        self.update_job = self.root.after(500, self.trigger_analysis)
    
    def initial_load(self):
        """初始加载数据"""
        if self.validate_dates(show_error=False):
            self.trigger_analysis()

    def validate_dates(self, show_error=True):
        """日期有效性验证"""
        try:
            start = self.start_date_picker.get_date()
            end = self.end_date_picker.get_date()
            
            if not start or not end:
                if show_error: messagebox.showerror("错误", "请选择有效日期")
                return False
                
            if start >= end:
                if show_error: messagebox.showerror("错误", "开始日期必须早于结束日期")
                return False
                
            return True
        except Exception as e:
            if show_error: messagebox.showerror("日期错误", str(e))
            return False
    
    def trigger_analysis(self):
        """触发分析前的验证"""
        if not self.validate_dates(show_error=True):
            return
            
        current_params = (
            self.index_var.get(),
            self.start_date_picker.get_date().strftime("%Y%m%d"),
            self.end_date_picker.get_date().strftime("%Y%m%d")
        )
        
        if current_params == self.last_update_params:
            return
            
        self.last_update_params = current_params
        self.start_full_analysis()
    
    def start_full_analysis(self):
        """更新后的分析方法（完全移除按钮状态控制）"""
        try:
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            
            analysis_thread = threading.Thread(
                target=self.perform_full_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.status_var.set(f"分析启动失败: {str(e)}")
            self.logger.log(f"分析启动错误: {traceback.format_exc()}")
    
    def create_overview_cards(self):
        """创建数据分析概览卡片（完整实现）"""
        self.overview_frame = ttk.LabelFrame(self.root, text="数据分析概览", padding="10")
        self.overview_frame.pack(fill=tk.X, pady=(0, 10))

        # 配置网格布局
        self.overview_frame.grid_columnconfigure(0, weight=1)
        self.overview_frame.grid_columnconfigure(1, weight=1)
        self.overview_frame.grid_columnconfigure(2, weight=1)
        self.overview_frame.grid_columnconfigure(3, weight=1)

        # 主力资金统计卡片
        self.main_stats_card = ttk.LabelFrame(
            self.overview_frame,
            text="主力资金统计",
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.main_stats_card.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NSEW)
        
        ttk.Label(self.main_stats_card, textvariable=self.max_inflow, 
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.main_stats_card, textvariable=self.max_outflow,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.main_stats_card, textvariable=self.avg_inflow,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.main_stats_card, textvariable=self.total_inflow,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)

        # 成交额统计卡片
        self.turnover_stats_card = ttk.LabelFrame(
            self.overview_frame,
            text="成交额统计",
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.turnover_stats_card.grid(row=0, column=1, padx=5, pady=5, sticky=tk.NSEW)
        
        ttk.Label(self.turnover_stats_card, textvariable=self.max_turnover,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.turnover_stats_card, textvariable=self.min_turnover,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.turnover_stats_card, textvariable=self.avg_turnover,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)
        ttk.Label(self.turnover_stats_card, textvariable=self.total_turnover,
                font=("SimHei", 10), anchor="w").pack(fill=tk.X)

        # 北向资金相关性卡片
        self.north_card = ttk.LabelFrame(
            self.overview_frame,
            text="北向资金相关性", 
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.north_card.grid(row=0, column=2, padx=5, pady=5, sticky=tk.NSEW)
        self.north_value = tk.StringVar(value="--")
        ttk.Label(self.north_card, textvariable=self.north_value, 
                font=("SimHei", 16, "bold")).pack(pady=5)
        ttk.Label(self.north_card, text="与指数涨跌幅相关性").pack()

        # 资金协同性卡片
        self.fund_card = ttk.LabelFrame(
            self.overview_frame,
            text="资金协同性",
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.fund_card.grid(row=0, column=3, padx=5, pady=5, sticky=tk.NSEW)
        self.fund_value = tk.StringVar(value="--")
        ttk.Label(self.fund_card, textvariable=self.fund_value,
                font=("SimHei", 16, "bold")).pack(pady=5)
        ttk.Label(self.fund_card, text="北向与主力资金相关性").pack()

    def update_overview_cards(self, correlations, analyze_north, analyze_main):
        """更新概览卡片显示（完整实现）"""
        # 主力资金相关性
        if 'main' in correlations:
            self.main_value.set(f"{correlations['main']:.4f}")
            color = "#008000" if correlations['main'] > 0.3 else "#FF0000" if correlations['main'] < -0.3 else "#000000"
            self.main_stats_card.configure(style=f"{'Positive' if correlations['main'] > 0 else 'Negative' if correlations['main'] < 0 else 'Neutral'}.TLabelframe")

        # 北向资金相关性
        if analyze_north and 'north' in correlations:
            self.north_value.set(f"{correlations['north']:.4f}")
            color = "#008000" if correlations['north'] > 0.3 else "#FF0000" if correlations['north'] < -0.3 else "#000000"
            self.north_card.configure(style=f"{'Positive' if correlations['north'] > 0 else 'Negative' if correlations['north'] < 0 else 'Neutral'}.TLabelframe")

        # 资金协同性
        if analyze_north and 'fund' in correlations:
            self.fund_value.set(f"{correlations['fund']:.4f}")
            color = "#008000" if correlations['fund'] > 0.5 else "#FF0000" if correlations['fund'] < -0.5 else "#000000"
            self.fund_card.configure(style=f"{'Positive' if correlations['fund'] > 0 else 'Negative' if correlations['fund'] < 0 else 'Neutral'}.TLabelframe")

    def update_stats(self, data, analyze_north, analyze_main):
        """更新统计信息（完整实现）"""
        # 主力资金统计
        if 'main_net_inflow' in data.columns:
            valid_main = data[data['main_net_inflow'].notna()]
            if valid_main.empty:
                self.max_inflow.set("最大净流入: --")
                self.max_outflow.set("最大净流出: --")
                self.avg_inflow.set("平均净流入: --")
                self.total_inflow.set("总净流入: --")
            else:
                self.max_inflow.set(f"最大净流入: {valid_main['main_net_inflow'].max():.2f}亿")
                self.max_outflow.set(f"最大净流出: {valid_main['main_net_inflow'].min():.2f}亿")
                self.avg_inflow.set(f"平均净流入: {valid_main['main_net_inflow'].mean():.2f}亿")
                self.total_inflow.set(f"总净流入: {valid_main['main_net_inflow'].sum():.2f}亿")

        # 成交额统计
        if 'total_turnover' in data.columns:
            valid_turnover = data[data['total_turnover'].notna()]
            if valid_turnover.empty:
                self.max_turnover.set("最大成交额: --")
                self.min_turnover.set("最小成交额: --")
                self.avg_turnover.set("平均成交额: --")
                self.total_turnover.set("总成交额: --")
            else:
                self.max_turnover.set(f"最大成交额: {valid_turnover['total_turnover'].max():.2f}亿")
                self.min_turnover.set(f"最小成交额: {valid_turnover['total_turnover'].min():.2f}亿")
                self.avg_turnover.set(f"平均成交额: {valid_turnover['total_turnover'].mean():.2f}亿")
                self.total_turnover.set(f"总成交额: {valid_turnover['total_turnover'].sum()/10000:.2f}万亿")
    
    def create_stats_cards(self):
        """创建统计信息卡片（完整实现版）"""
        # 清空原有卡片
        for widget in self.stats_frame.winfo_children():
            widget.destroy()

        # 配置网格布局
        self.stats_frame.grid_columnconfigure(0, weight=1)
        self.stats_frame.grid_columnconfigure(1, weight=1)
        self.stats_frame.grid_rowconfigure(0, weight=1)
        self.stats_frame.grid_rowconfigure(1, weight=1)

        # ================== 主力资金统计卡片 ==================
        self.main_stats_card = ttk.LabelFrame(
            self.stats_frame,
            text="主力资金统计",
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.main_stats_card.grid(
            row=0, column=0,
            padx=10, pady=10,
            sticky=tk.NSEW
        )

        # 配置内部布局
        ttk.Label(
            self.main_stats_card,
            textvariable=self.max_inflow,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.main_stats_card,
            textvariable=self.max_outflow,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.main_stats_card,
            textvariable=self.avg_inflow,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.main_stats_card,
            textvariable=self.total_inflow,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)

        # ================== 成交额统计卡片 ==================
        self.turnover_stats_card = ttk.LabelFrame(
            self.stats_frame,
            text="成交额统计",
            padding=(15, 10),
            style="Stats.TLabelframe"
        )
        self.turnover_stats_card.grid(
            row=0, column=1,
            padx=10, pady=10,
            sticky=tk.NSEW
        )

        ttk.Label(
            self.turnover_stats_card,
            textvariable=self.max_turnover,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.turnover_stats_card,
            textvariable=self.min_turnover,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.turnover_stats_card,
            textvariable=self.avg_turnover,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)
        
        ttk.Label(
            self.turnover_stats_card,
            textvariable=self.total_turnover,
            font=("SimHei", 12),
            style="Value.TLabel"
        ).pack(fill=tk.X, pady=3)

        # ================== 样式配置 ==================
        style = ttk.Style()
        style.configure("Stats.TLabelframe", 
                    borderwidth=2,
                    relief="groove",
                    foreground="#333333")
        
        style.configure("Value.TLabel",
                    anchor="w",
                    padding=(5, 2))
        
        style.map("Stats.TLabelframe",
                foreground=[('active', '#0055AA')],
                bordercolor=[('active', '#0055AA')])  
    
    def show_logs(self):
        """显示日志窗口"""
        log_window = tk.Toplevel(self.root)
        log_window.title("运行日志")
        log_window.geometry("800x600")
        
        log_text = scrolledtext.ScrolledText(log_window, wrap=tk.WORD)
        log_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 插入日志
        log_text.insert(tk.END, self.logger.get_logs())
        
        # 使文本只读
        log_text.configure(state=tk.DISABLED)      

    def perform_full_analysis(self, start_date, end_date):
        """核心分析方法"""
        try:
            # 更新状态
            self.root.after(0, lambda: self.status_var.set("正在加载基础数据..."))
            
            # 获取指数数据
            index_code = self.index_var.get()
            index_data = self.get_market_index_data(index_code, start_date, end_date)
            
            if index_data is None or index_data.empty:
                raise Exception("获取指数数据失败")
                
            # 快速更新预览
            self.root.after(0, self.partial_update(index_data))
            
            # 获取其他数据
            self.root.after(0, lambda: self.status_var.set("正在加载资金数据..."))
            main_data = self.get_main_money_flow_data(start_date, end_date)
            north_data = self.get_north_flow_data(start_date, end_date) if self.north_enabled.get() else None
            
            self.root.after(0, lambda: self.status_var.set("正在加载成交额..."))
            turnover_data = self.get_market_turnover(start_date.date(), end_date.date())
            
            # 合并数据
            merged_data = self.merge_data(
                index_data, north_data, main_data, 
                self.north_enabled.get(), main_data is not None,
                turnover_data
            )
            
            # 计算相关性
            correlations = self.calculate_correlations(merged_data, 
                self.north_enabled.get(), main_data is not None)
            
            # 完整更新
            self.root.after(0, lambda: self.full_update(merged_data, correlations))
            
        except Exception as e:
            self.root.after(0, lambda: self.status_var.set(f"分析失败: {str(e)}"))
            import traceback  # 确保导入
            self.logger.log(f"分析错误: {traceback.format_exc()}")

    def full_update(self, merged_data, correlations):
        """完整数据更新"""
        try:
            # 更新所有图表
            self.plot_index_trend(merged_data, self.index_var.get())
            self.plot_fund_flow_trend(merged_data, 
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            self.plot_turnover_trend(merged_data)
            self.plot_correlation_scatter(merged_data, 
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            
            # 更新数据表格
            self.current_data = merged_data
            self.update_data_table(merged_data)
            
            # 更新统计信息
            self.update_overview_cards(correlations, 
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            self.update_stats(merged_data, 
                self.north_enabled.get(), 'main_net_inflow' in merged_data.columns)
            
            # 分析交割日影响
            if not merged_data.empty:
                self.analyze_delivery_impact()
            
            self.status_var.set(f"分析完成（{len(merged_data)}个交易日）")
        except Exception as e:
            self.logger.log(f"完整更新失败: {str(e)}")

    def partial_update(self, index_data):
        """快速更新预览数据"""
        def update():
            try:
                # 更新指数图表
                self.plot_index_trend(index_data, self.index_var.get())
                
                # 更新基础数据表格
                self.current_data = index_data[['date', 'close', 'pct_change']]
                self.update_data_table(self.current_data)
                
                # 更新概览卡片
                self.main_value.set("--")
                self.north_value.set("--")
                self.fund_value.set("--")
                self.turnover_value.set("--")
                
                self.status_var.set(f"已加载基础数据（{len(index_data)}天）")
            except Exception as e:
                self.logger.log(f"快速更新失败: {str(e)}")
        return update
    
    def get_market_index_data(self, index_code, start_date, end_date):
        """获取指定指数的历史数据（带本地文件缓存）"""
        filename = os.path.join(self.cache_dir, f"index_{index_code}.csv")
        local_data = pd.DataFrame()

        # 尝试读取本地缓存
        if os.path.exists(filename):
            try:
                local_data = pd.read_csv(filename, parse_dates=['date'])
                self.logger.log(f"从本地缓存加载 {index_code} 数据（{len(local_data)} 条）")
                
                # 检查缓存是否覆盖需求
                cache_start = local_data['date'].min()
                cache_end = local_data['date'].max()
                if cache_start <= start_date and cache_end >= end_date:
                    filtered = local_data[(local_data['date'] >= start_date) & (local_data['date'] <= end_date)]
                    filtered['pct_change'] = filtered['close'].pct_change() * 100
                    self.logger.log(f"本地缓存满足需求，返回 {len(filtered)} 条数据")
                    return filtered
            except Exception as e:
                self.logger.log(f"读取缓存文件失败: {e}")

        # 从API获取数据
        try:
            # 根据指数代码获取数据
            if index_code in ["sh000001", "sz399001", "sz399006", "sh000688", "sh000300"]:
                df = ak.stock_zh_index_daily(symbol=index_code)
            else:
                self.logger.log(f"暂不支持该指数: {index_code}")
                return pd.DataFrame()

            if df is None or df.empty:
                self.logger.log(f"API返回空数据: {index_code}")
                return pd.DataFrame()

            # 处理API数据
            df['date'] = pd.to_datetime(df['date'])
            api_data = df[(df['date'] >= start_date) & (df['date'] <= end_date)]
            
            if not api_data.empty:
                # 合并本地缓存和API数据
                combined = pd.concat([local_data, api_data]).drop_duplicates('date').sort_values('date')
                
                # 保存更新后的缓存
                combined.to_csv(filename, index=False)
                self.logger.log(f"更新本地缓存 {filename}（新增 {len(api_data)} 条）")
                
                # 计算涨跌幅
                combined['pct_change'] = combined['close'].pct_change() * 100
                return combined
            else:
                self.logger.log("API数据在请求范围内为空")
                return pd.DataFrame()
            
        except Exception as e:
            self.logger.log(f"获取指数数据失败: {str(e)}")
            return pd.DataFrame()
    
    def get_north_flow_data(self, start_date, end_date):
        """获取北向资金净流入数据（兼容多种akshare版本）"""
        try:
            # 尝试最新接口
            try:
                df = ak.stock_hsgt_north_net_flow_in_em()
                if df is None or df.empty:
                    raise Exception("获取北向资金数据失败")
                    
                df['date'] = pd.to_datetime(df['date'])
                df = df[(df['date'] >= start_date) & (df['date'] <= end_date)]
                df = df.rename(columns={'net_amount': 'north_net_inflow'})
                # 转换为亿元单位
                df['north_net_inflow'] = df['north_net_inflow'] / 100000000
                
                self.logger.log(f"使用新接口获取北向资金数据成功，共{len(df)}条记录")
                return df[['date', 'north_net_inflow']]
            except Exception as e1:
                self.logger.log(f"尝试最新北向资金接口失败: {e1}")
                
                # 尝试旧接口
                try:
                    df = ak.stock_individual_fund_flow_rank()
                    if df is None or df.empty:
                        raise Exception("获取北向资金数据失败")
                        
                    df['date'] = pd.to_datetime(df['日期'])
                    df = df[(df['date'] >= start_date) & (df['date'] <= end_date)]
                    df = df.rename(columns={'主力净流入-净额': 'north_net_inflow'})
                    
                    self.logger.log(f"使用旧接口1获取北向资金数据成功，共{len(df)}条记录")
                    return df[['date', 'north_net_inflow']]
                except Exception as e2:
                    self.logger.log(f"尝试旧北向资金接口失败: {e2}")
                    
                    # 尝试备选接口
                    try:
                        df = ak.stock_market_fund_flow()
                        if df is None or df.empty:
                            raise Exception("获取北向资金数据失败")
                            
                        df['date'] = pd.to_datetime(df['日期'])
                        df = df[(df['date'] >= start_date) & (df['date'] <= end_date)]
                        df = df.rename(columns={'主力净流入-净额': 'north_net_inflow'})
                        
                        self.logger.log(f"使用旧接口2获取北向资金数据成功，共{len(df)}条记录")
                        return df[['date', 'north_net_inflow']]
                    except Exception as e3:
                        self.logger.log(f"尝试备选北向资金接口失败: {e3}")
                        raise Exception("无法获取北向资金数据，请确保akshare版本是最新的")
        except Exception as e:
            self.logger.log(f"获取北向资金数据时出错: {e}")
            return None
    
    def get_main_money_flow_data(self, start_date, end_date):
        """获取主力资金数据（带本地缓存）"""
        filename = os.path.join(self.cache_dir, "main_money_flow.csv")
        local_data = pd.DataFrame()

        # 尝试读取本地缓存
        if os.path.exists(filename):
            try:
                local_data = pd.read_csv(filename, parse_dates=['date'])
                local_data = local_data[(local_data['date'] >= start_date) & 
                                    (local_data['date'] <= end_date)]
                self.logger.log(f"从本地缓存加载主力资金数据（{len(local_data)} 条）")
            except Exception as e:
                self.logger.log(f"读取主力资金缓存失败: {e}")

        # 检查本地数据是否满足需求
        if not local_data.empty:
            cache_start = local_data['date'].min()
            cache_end = local_data['date'].max()
            if cache_start <= start_date and cache_end >= end_date:
                return local_data

        # 从API获取数据（兼容多个接口）
        api_data = pd.DataFrame()
        try:
            # 尝试stock_market_fund_flow接口
            try:
                df = ak.stock_market_fund_flow()
                if df is not None and not df.empty:
                    df['date'] = pd.to_datetime(df['日期'])
                    df = df.rename(columns={'主力净流入-净额': 'main_net_inflow'})
                    # 转换单位（假设原始数据为元）
                    df['main_net_inflow'] = df['main_net_inflow'] / 100000000
                    api_data = df[['date', 'main_net_inflow']]
            except Exception as e:
                self.logger.log(f"接口1获取失败: {e}")

            # 如果第一个接口失败，尝试stock_individual_fund_flow
            if api_data.empty:
                try:
                    df = ak.stock_individual_fund_flow(stock="all")
                    if df is not None and not df.empty:
                        # 按日期聚合
                        df['date'] = pd.to_datetime(df['日期'])
                        grouped = df.groupby('date')['主力净流入-净额'].sum().reset_index()
                        grouped = grouped.rename(columns={'主力净流入-净额': 'main_net_inflow'})
                        # 转换单位
                        grouped['main_net_inflow'] = grouped['main_net_inflow'] / 100000000
                        api_data = grouped[['date', 'main_net_inflow']]
                except Exception as e:
                    self.logger.log(f"接口2获取失败: {e}")

            # 过滤API数据日期范围
            if not api_data.empty:
                api_data = api_data[(api_data['date'] >= start_date) & 
                                (api_data['date'] <= end_date)]
                self.logger.log(f"从API获取主力资金数据 {len(api_data)} 条")

                # 合并本地缓存和API数据
                combined = pd.concat([local_data, api_data])
                combined = combined.drop_duplicates('date').sort_values('date')

                # 保存更新后的缓存
                try:
                    # 读取完整缓存数据
                    full_cache = pd.DataFrame()
                    if os.path.exists(filename):
                        full_cache = pd.read_csv(filename, parse_dates=['date'])
                    
                    # 合并新旧数据
                    updated_cache = pd.concat([full_cache, combined])
                    updated_cache = updated_cache.drop_duplicates('date')
                    updated_cache.to_csv(filename, index=False)
                    self.logger.log(f"主力资金缓存已更新，总数据量 {len(updated_cache)} 条")
                except Exception as e:
                    self.logger.log(f"保存主力资金缓存失败: {e}")

                return combined

            return local_data  # 返回本地缓存数据（即使不完整）
        except Exception as e:
            self.logger.log(f"获取主力资金数据失败: {str(e)}")
            return local_data  # 返回本地缓存数据（如果有）
    
    def get_market_turnover(self, start_date: date, end_date: date) -> pd.DataFrame:
        """获取成交额数据（修复缓存问题）"""
        try:
            cache_key = (start_date, end_date)
            
            # 检查缓存
            if cache_key in self.turnover_cache:
                cached_data = self.turnover_cache[cache_key]
                if not cached_data.empty and 'date' in cached_data.columns:
                    self.logger.log(f"使用缓存成交额数据 {start_date} 至 {end_date}")
                    return cached_data.copy()
                    
            # 获取新数据
            self.logger.log(f"请求API获取成交额数据 {start_date} 至 {end_date}")
            new_data = self._fetch_full_turnover_data(start_date, end_date)
            
            # 验证数据
            if new_data.empty or 'date' not in new_data.columns:
                raise ValueError("获取的成交额数据无效")
                
            # 更新缓存
            self.turnover_cache[cache_key] = new_data
            self._clean_turnover_cache()
            
            return new_data.copy()
            
        except Exception as e:
            self.logger.log(f"获取成交额数据失败: {str(e)}")
            return pd.DataFrame()
    
    def fetch_sse_turnover(self, date_str: str) -> float:
        """获取上交所成交额（亿元），带重试机制"""
        max_retries = 3
        retry_delay = 1  # 秒

        for attempt in range(max_retries):
            try:
                df = ak.stock_sse_deal_daily(date=date_str)
                if df is None:
                    raise ValueError("API返回空数据")

                # 验证数据列
                if "单日情况" not in df.columns or "股票" not in df.columns:
                    raise ValueError("API返回数据结构异常")

                # 查找成交金额行（注意繁体字）
                amount_row = df[df["单日情况"] == "成交金額"]
                if amount_row.empty:
                    return 0.0

                value = amount_row["股票"].values[0]
                if not isinstance(value, (int, float)):
                    raise ValueError(f"非数值类型: {type(value)}")

                return round(float(value), 2)
                
            except Exception as e:
                if attempt < max_retries - 1:
                    self.logger.log(f"上交所{date_str} 第{attempt+1}次重试...")
                    time.sleep(retry_delay * (attempt + 1))
                    continue
                self.logger.log(f"获取上交所 {date_str} 成交额失败: {str(e)}")
                return 0.0

    def fetch_szse_turnover(self, date_str: str) -> float:
        """获取深交所成交额（亿元），带数据校验"""
        max_retries = 2
        retry_delay = 2  # 秒

        for attempt in range(max_retries):
            try:
                df = ak.stock_szse_summary(date=date_str)
                if df is None:
                    raise ValueError("API返回空数据")

                # 验证必要列存在
                required_cols = {"证券类别", "成交金额"}
                if not required_cols.issubset(df.columns):
                    missing = required_cols - set(df.columns)
                    raise ValueError(f"缺失列: {missing}")

                # 筛选股票数据
                stock_row = df[df["证券类别"] == "股票"]
                if stock_row.empty:
                    return 0.0

                # 提取并转换数据
                amount_yuan = stock_row["成交金额"].values[0]
                if not isinstance(amount_yuan, (int, float)):
                    raise ValueError(f"非数值类型: {type(amount_yuan)}")

                return round(float(amount_yuan) / 100000000, 2)
                
            except Exception as e:
                if attempt < max_retries - 1:
                    self.logger.log(f"深交所{date_str} 第{attempt+1}次重试...")
                    time.sleep(retry_delay * (attempt + 1))
                    continue
                self.logger.log(f"获取深交所 {date_str} 成交额失败: {str(e)}")
                return 0.0
    
    def _get_cached_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
        """尝试从现有缓存中获取部分或全部成交额数据"""
        # 查找包含在请求范围内的缓存项
        overlapping_caches = []
        for (cache_start, cache_end), cache_data in self.turnover_cache.items():
            if (cache_start <= end_date) and (cache_end >= start_date):
                overlapping_caches.append((cache_start, cache_end, cache_data))
        
        if not overlapping_caches:
            return None
            
        # 合并所有重叠的缓存数据
        all_cached_data = []
        for cache_start, cache_end, cache_data in overlapping_caches:
            # 截取在请求范围内的数据
            cache_in_range = cache_data[
                (pd.to_datetime(cache_data['date']).dt.date >= start_date) & 
                (pd.to_datetime(cache_data['date']).dt.date <= end_date)
            ]
            all_cached_data.append(cache_in_range)
        
        if not all_cached_data:
            return None
            
        # 合并并去重
        merged = pd.concat(all_cached_data, ignore_index=True)
        merged = merged.sort_values('date').drop_duplicates('date')
        
        return merged
    
    def _fetch_missing_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
        """获取缺失的成交额数据"""
        # 原有的获取成交额数据的核心逻辑
        trade_dates = self.trading_day_checker.get_trading_days(start_date, end_date)
        dates_str = [d.strftime("%Y%m%d") for d in trade_dates]
        sse_data = []
        szse_data = []
        
        total_dates = len(dates_str)
        for i, date_str in enumerate(dates_str):
            self.logger.log(f"正在获取{date_str}成交额数据 ({i+1}/{total_dates})")
            
            sse = self.fetch_sse_turnover(date_str)
            szse = self.fetch_szse_turnover(date_str)
            
            sse_data.append((date_str, sse))
            szse_data.append((date_str, szse))
        
        # 转换为DataFrame
        sse_df = pd.DataFrame(sse_data, columns=['date_str', 'sse_turnover'])
        szse_df = pd.DataFrame(szse_data, columns=['date_str', 'szse_turnover'])
        
        # 合并数据
        merged = pd.merge(sse_df, szse_df, on='date_str', how='outer')
        merged['date'] = pd.to_datetime(merged['date_str'])
        merged['total_turnover'] = merged['sse_turnover'] + merged['szse_turnover']
        
        return merged[['date', 'sse_turnover', 'szse_turnover', 'total_turnover']]
    
    def _fetch_full_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
        # 获取原始数据
        trade_dates = self.trading_day_checker.get_trading_days(start_date, end_date)
        dates_str = [d.strftime("%Y%m%d") for d in trade_dates]
        
        sse_data = []
        szse_data = []
        
        for date_str in dates_str:
            # 获取上交所和深交所成交额
            sse = self.fetch_sse_turnover(date_str)
            szse = self.fetch_szse_turnover(date_str)
            
            sse_data.append((date_str, sse))
            szse_data.append((date_str, szse))
        
        # 创建DataFrame并规范列名
        df = pd.DataFrame({
            'date_str': [d[0] for d in sse_data],
            'sse_turnover': [d[1] for d in sse_data],
            'szse_turnover': [d[1] for d in szse_data]
        })
        
        # 添加标准日期列
        df['date'] = pd.to_datetime(df['date_str'], format='%Y%m%d')
        df['total_turnover'] = df['sse_turnover'] + df['szse_turnover']
        
        # 删除临时列
        df.drop(columns=['date_str'], inplace=True)
        
        # 按日期排序
        df.sort_values('date', inplace=True)
        
        return df[['date', 'sse_turnover', 'szse_turnover', 'total_turnover']]
    
    def merge_data(self, index_data, north_data, main_data, analyze_north, analyze_main, turnover_data):
        """合并数据（增强版）"""
        # 使用最新指数数据作为基准
        merged = index_data[['date', 'close', 'pct_change']].copy()
        
        # 保留已有成交额数据（如果存在）
        if hasattr(self, 'turnover_data') and self.turnover_data is not None:
            merged = pd.merge(
                merged,
                self.turnover_data[['date', 'total_turnover']],
                on='date',
                how='left'
            )
        
        # 合并主力资金（允许部分更新）
        if analyze_main and main_data is not None:
            merged = pd.merge(
                merged,
                main_data[['date', 'main_net_inflow']],
                on='date',
                how='left'
            )
        
        # 合并北向资金（允许部分更新）
        if analyze_north and north_data is not None:
            merged = pd.merge(
                merged,
                north_data[['date', 'north_net_inflow']],
                on='date',
                how='left'
            )
        
        return merged.drop_duplicates('date').sort_values('date')
    
    def calculate_correlations(self, data, analyze_north, analyze_main):
        """计算各种资金流向与指数涨跌幅的相关系数"""
        correlations = {}
        
        try:
            # 计算主力资金与指数涨跌幅的相关性
            if 'main_net_inflow' in data.columns and 'pct_change' in data.columns:
                main_corr = data['main_net_inflow'].corr(data['pct_change'])
                correlations['main'] = main_corr
                self.logger.log(f"主力资金与指数涨跌幅的相关系数: {main_corr:.4f}")
            
            # 计算北向资金与指数涨跌幅的相关性
            if analyze_north and 'north_net_inflow' in data.columns and 'pct_change' in data.columns:
                north_corr = data['north_net_inflow'].corr(data['pct_change'])
                correlations['north'] = north_corr
                self.logger.log(f"北向资金与指数涨跌幅的相关系数: {north_corr:.4f}")
            
            # 计算北向资金与主力资金的相关性
            if analyze_north and 'north_net_inflow' in data.columns and 'main_net_inflow' in data.columns:
                fund_corr = data['north_net_inflow'].corr(data['main_net_inflow'])
                correlations['fund'] = fund_corr
                self.logger.log(f"北向资金与主力资金的相关系数: {fund_corr:.4f}")
            
            # 计算成交额与指数涨跌幅的相关性
            if 'total_turnover' in data.columns and 'pct_change' in data.columns:
                turnover_corr = data['total_turnover'].corr(data['pct_change'])
                correlations['turnover'] = turnover_corr
                self.logger.log(f"成交额与指数涨跌幅的相关系数: {turnover_corr:.4f}")
            
            return correlations
        except Exception as e:
            self.logger.log(f"计算相关系数时出错: {e}")
            return correlations
    
    def update_results(self, data, index_name, correlations, analyze_north, analyze_main):
        """更新分析结果图表和数据"""
        # 数据完整性警告
        warning_msg = []
        if 'main_net_inflow' in data.columns:
            na_count = data['main_net_inflow'].isna().sum()
            total_days = len(data)
            if na_count > 0:
                warning_msg.append(f"主力资金缺失{na_count}/{total_days}个交易日数据")
        
        if warning_msg:
            messagebox.showwarning("数据不完整", 
                "以下数据存在缺失:\n? " + "\n? ".join(warning_msg))
        
        try:
            # 更新指数趋势图
            self.plot_index_trend(data, index_name)
            # 更新拆分后的趋势图
            self.plot_fund_flow_trend(data, analyze_north, analyze_main)
            self.plot_turnover_trend(data)
            # 更新散点图
            self.plot_correlation_scatter(data, analyze_north, analyze_main)
            # 更新数据表格
            self.update_data_table(data)
            # 更新概览卡片
            self.update_overview_cards(correlations, analyze_north, analyze_main)
            # 更新统计信息
            self.update_stats(data, analyze_north, analyze_main)
            # 切换到指数趋势选项卡
            self.notebook.select(0)
        except Exception as e:
            self.logger.log(f"更新结果时出错: {e}")
            messagebox.showerror("更新结果失败", str(e))
    
    def plot_index_trend(self, data, index_name):
        """绘制指数趋势图"""
        self.index_ax.clear()
        
        # 绘制收盘价
        self.index_ax.plot(data['date'], data['close'], color='blue', linewidth=2, label='收盘价')
        
        # 添加涨跌标记
        for i in range(1, len(data)):
            if data.iloc[i]['close'] > data.iloc[i-1]['close']:

                # 添加交割日标记
                if not data.empty:
                    start_date = data['date'].min().date()
                    end_date = data['date'].max().date()
                    delivery_dates = self.delivery_calculator.get_monthly_delivery_dates(start_date, end_date)
                    
                    for delivery_date in delivery_dates:
                        delivery_datetime = datetime.combine(delivery_date, datetime.min.time())
                        if delivery_datetime >= data['date'].min() and delivery_datetime <= data['date'].max():
                            self.index_ax.axvline(x=delivery_datetime, color='orange', linestyle='--', linewidth=1, alpha=0.7)
                            self.index_ax.text(delivery_datetime, self.index_ax.get_ylim()[1]*0.95,
                                            '交割日', rotation=90, color='orange', 
                                            verticalalignment='top', alpha=0.7)
                self.index_ax.scatter(data.iloc[i]['date'], data.iloc[i]['close'], color='red', marker='^', s=50)
            elif data.iloc[i]['close'] < data.iloc[i-1]['close']:
                self.index_ax.scatter(data.iloc[i]['date'], data.iloc[i]['close'], color='green', marker='v', s=50)
        
        # 设置图表标题和标签
        self.index_ax.set_title(f"{index_name}趋势图")
        self.index_ax.set_xlabel("日期")
        self.index_ax.set_ylabel("指数")
        
        # 添加网格线
        self.index_ax.grid(True, linestyle='--', alpha=0.7)
        
        # 添加图例
        self.index_ax.legend(loc='upper left')
        
        # 设置日期格式化
        from matplotlib.dates import DateFormatter
        date_format = DateFormatter('%m-%d')
        self.index_ax.xaxis.set_major_formatter(date_format)
        
        # 自动调整布局
        self.index_fig.tight_layout()
        self.index_canvas.draw()
    
    def plot_fund_flow_trend(self, data, analyze_north=True, analyze_main=True):
        """绘制资金流向趋势图（完整实现）"""
        self.fund_flow_ax.clear()
        ax = self.fund_flow_ax
        
        try:
            # 主力资金柱状图（带颜色区分）
            if analyze_main and 'main_net_inflow' in data.columns:
                # 生成颜色列表（红涨绿跌）
                colors = np.where(data['main_net_inflow'] >= 0, '#FF4444', '#44FF44')
                bars = ax.bar(data['date'], data['main_net_inflow'], 
                            width=0.8, color=colors, alpha=0.7,
                            label='主力资金净流入')
                
                # 添加统计信息标注
                valid_main = data['main_net_inflow'].dropna()
                if not valid_main.empty:
                    stats_text = (
                        f"主力资金统计\n"
                        f"最大流入: {valid_main.max():.2f}亿\n"
                        f"最大流出: {valid_main.min():.2f}亿\n"
                        f"平均值: {valid_main.mean():.2f}亿"
                    )
                    ax.text(0.98, 0.95, stats_text, transform=ax.transAxes,
                        ha='right', va='top', 
                        bbox=dict(facecolor='white', alpha=0.8, edgecolor='gray'))

            # 北向资金折线图（带动态颜色）
            if analyze_north and 'north_net_inflow' in data.columns:
                ax2 = ax.twinx()
                # 生成动态颜色（绿涨红跌）
                colors = np.where(data['north_net_inflow'] >= 0, '#00AA00', '#AA0000')
                
                # 分段绘制不同颜色的线段
                for i in range(1, len(data)):
                    ax2.plot(data['date'].iloc[i-1:i+1], 
                            data['north_net_inflow'].iloc[i-1:i+1],
                            color=colors[i], linewidth=2, label='北向资金净流入')
                
                # 避免重复标签
                handles, labels = ax2.get_legend_handles_labels()
                if handles:
                    ax2.legend([handles[0]], [labels[0]], loc='upper right')

                # 添加统计信息标注
                valid_north = data['north_net_inflow'].dropna()
                if not valid_north.empty:
                    stats_text = (
                        f"北向资金统计\n"
                        f"最大流入: {valid_north.max():.2f}亿\n"
                        f"最大流出: {valid_north.min():.2f}亿\n"
                        f"平均值: {valid_north.mean():.2f}亿"
                    )
                    ax2.text(0.98, 0.75, stats_text, transform=ax.transAxes,
                            ha='right', va='top', 
                            bbox=dict(facecolor='white', alpha=0.8, edgecolor='gray'))
                
                ax2.set_ylabel('北向资金 (亿元)', color='#555555')
                ax2.tick_params(axis='y', labelcolor='#555555')

            # 通用图表设置
            ax.set_title("资金流向趋势分析", fontsize=14, pad=20)
            ax.set_xlabel("日期", fontsize=12)
            ax.set_ylabel("主力资金 (亿元)", color='#1f77b4')
            ax.tick_params(axis='y', labelcolor='#1f77b4')
            
            # 日期格式化
            ax.xaxis.set_major_formatter(DateFormatter('%m-%d'))
            plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
            
            # 网格和样式
            ax.grid(True, linestyle='--', alpha=0.7, axis='y')
            ax.set_axisbelow(True)
            
            # 合并图例
            lines, labels = ax.get_legend_handles_labels()
            if analyze_north and 'north_net_inflow' in data.columns:
                lines2, labels2 = ax2.get_legend_handles_labels()
                lines += lines2
                labels += labels2
            if lines:
                ax.legend(lines, labels, loc='upper left', 
                        bbox_to_anchor=(0.02, 0.98), framealpha=0.9)

            self.fund_flow_fig.tight_layout()
            self.fund_flow_canvas.draw()

        except KeyError as e:
            self.logger.log(f"绘制资金流向趋势图失败，缺失关键列: {str(e)}")
        except Exception as e:
            self.logger.log(f"绘制资金流向趋势图时发生未知错误: {str(e)}")

    def plot_turnover_trend(self, data):
        """绘制成交额趋势图（增强版）"""
        self.turnover_ax.clear()
        
        # 验证数据
        if data is None or data.empty:
            self.turnover_ax.set_title("无有效数据")
            self.turnover_canvas.draw()
            return
            
        if 'date' not in data.columns or 'total_turnover' not in data.columns:
            self.turnover_ax.set_title("数据缺少必要列")
            self.turnover_canvas.draw()
            return
        
        try:
            from matplotlib.dates import DateFormatter
            # 绘制柱状图
            bars = self.turnover_ax.bar(
                data['date'], data['total_turnover'],
                color='#1f77b4', alpha=0.7,
                label='日成交额'
            )
            
            # 添加动态移动平均线
            if len(data) >= 5:
                ma_window = min(20, len(data)//2)  # 自适应窗口大小
                data['ma'] = data['total_turnover'].rolling(ma_window).mean()
                
                # 绘制MA线
                self.turnover_ax.plot(
                    data['date'], data['ma'],
                    color='#ff7f0e', linewidth=2,
                    label=f'{ma_window}日移动平均'
                )
                
                # 标注极值点
                max_idx = data['total_turnover'].idxmax()
                min_idx = data['total_turnover'].idxmin()
                
                self.turnover_ax.scatter(
                    data.loc[max_idx, 'date'], data.loc[max_idx, 'total_turnover'],
                    color='red', zorder=5, label='最大值'
                )
                self.turnover_ax.scatter(
                    data.loc[min_idx, 'date'], data.loc[min_idx, 'total_turnover'],
                    color='green', zorder=5, label='最小值'
                )


            if 'total_turnover' in data.columns:
                valid_turnover = data['total_turnover'].dropna()
                if not valid_turnover.empty:
                    stats_text = (
                        f"成交额统计\n"
                        f"最大值: {valid_turnover.max():.2f}亿\n"
                        f"最小值: {valid_turnover.min():.2f}亿\n"
                        f"平均值: {valid_turnover.mean():.2f}亿\n"
                        f"总成交额: {valid_turnover.sum()/10000:.2f}万亿"
                    )
                    self.turnover_ax.text(0.98, 0.95, stats_text, 
                                        transform=self.turnover_ax.transAxes,
                                        ha='right', va='top',
                                        bbox=dict(facecolor='white', alpha=0.8, edgecolor='gray'))
            
            # 优化图表显示
            self.turnover_ax.set_title("成交额趋势分析")
            self.turnover_ax.set_ylabel("成交额 (亿元)")
            from matplotlib.dates import DateFormatter
            self.turnover_ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))  # 明确使用全称
            self.turnover_ax.grid(True, linestyle='--', alpha=0.5)
            self.turnover_ax.legend(loc='upper left')
            
            # 自动旋转日期标签
            plt.setp(self.turnover_ax.get_xticklabels(), rotation=45, ha='right')
            
            self.turnover_fig.tight_layout()
            self.turnover_canvas.draw()
            
        except Exception as e:
            self.logger.log(f"绘制成交额图表失败: {str(e)}")
            self.turnover_ax.set_title("图表渲染错误")
            self.turnover_canvas.draw()
    
    def plot_correlation_scatter(self, data, analyze_north=True, analyze_main=True):
        """绘制相关性散点图（完整实现）"""
        self.scatter_ax.clear()
        
        try:
            has_data = False
            
            # 主力资金散点图
            if analyze_main and 'main_net_inflow' in data.columns and 'pct_change' in data.columns:
                main_valid = data.dropna(subset=['main_net_inflow', 'pct_change'])
                if not main_valid.empty:
                    # 绘制散点
                    scatter1 = self.scatter_ax.scatter(
                        main_valid['main_net_inflow'], 
                        main_valid['pct_change'],
                        c='#1f77b4', alpha=0.6, 
                        edgecolors='w', linewidths=0.5,
                        label='主力资金'
                    )
                    
                    # 计算回归线
                    m_main, b_main = np.polyfit(
                        main_valid['main_net_inflow'], 
                        main_valid['pct_change'], 
                        1
                    )
                    self.scatter_ax.plot(
                        main_valid['main_net_inflow'], 
                        m_main * main_valid['main_net_inflow'] + b_main,
                        color='#1f77b4', linestyle='--', linewidth=2,
                        label=f'主力回归线 (y={m_main:.2f}x+{b_main:.2f})'
                    )
                    has_data = True

            # 北向资金散点图
            if analyze_north and 'north_net_inflow' in data.columns and 'pct_change' in data.columns:
                north_valid = data.dropna(subset=['north_net_inflow', 'pct_change'])
                if not north_valid.empty:
                    # 绘制散点
                    scatter2 = self.scatter_ax.scatter(
                        north_valid['north_net_inflow'], 
                        north_valid['pct_change'],
                        c='#ff7f0e', alpha=0.6,
                        marker='s', edgecolors='w',
                        linewidths=0.5, label='北向资金'
                    )
                    
                    # 计算回归线
                    m_north, b_north = np.polyfit(
                        north_valid['north_net_inflow'], 
                        north_valid['pct_change'], 
                        1
                    )
                    self.scatter_ax.plot(
                        north_valid['north_net_inflow'], 
                        m_north * north_valid['north_net_inflow'] + b_north,
                        color='#ff7f0e', linestyle='-.', linewidth=2,
                        label=f'北向回归线 (y={m_north:.2f}x+{b_north:.2f})'
                    )
                    has_data = True

            if not has_data:
                self.scatter_ax.text(0.5, 0.5, '无有效数据', 
                                    ha='center', va='center', 
                                    transform=self.scatter_ax.transAxes,
                                    fontsize=14, color='gray')
                return

            # 图表装饰
            self.scatter_ax.set_title('资金流向与指数涨跌幅相关性分析', fontsize=14, pad=20)
            self.scatter_ax.set_xlabel('资金净流入 (亿元)', fontsize=12)
            self.scatter_ax.set_ylabel('指数涨跌幅 (%)', fontsize=12)
            
            # 网格线设置
            self.scatter_ax.grid(True, linestyle='--', alpha=0.6)
            
            # 图例设置
            handles, labels = self.scatter_ax.get_legend_handles_labels()
            if handles:
                self.scatter_ax.legend(
                    handles, labels,
                    loc='upper left',
                    bbox_to_anchor=(0.02, 0.98),
                    frameon=True,
                    framealpha=0.9,
                    edgecolor='#ddd'
                )

            # 相关系数标注
            if analyze_main and 'main' in self.correlations:
                self.scatter_ax.annotate(
                    f'主力相关系数: {self.correlations["main"]:.2f}',
                    xy=(0.98, 0.15), xycoords='axes fraction',
                    ha='right', va='bottom',
                    color='#1f77b4',
                    bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)
                )
            
            if analyze_north and 'north' in self.correlations:
                self.scatter_ax.annotate(
                    f'北向相关系数: {self.correlations["north"]:.2f}',
                    xy=(0.98, 0.05), xycoords='axes fraction',
                    ha='right', va='bottom',
                    color='#ff7f0e',
                    bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)
                )

            self.scatter_fig.tight_layout()
            self.scatter_canvas.draw()

        except KeyError as e:
            self.logger.log(f"绘制散点图失败，缺失关键列: {str(e)}")
        except Exception as e:
            self.logger.log(f"绘制散点图时发生未知错误: {str(e)}")
    
    def update_data_table(self, data):
        """更新数据表格"""
        # 清除现有数据
        for item in self.data_tree.get_children():
            self.data_tree.delete(item)
        
        # 设置列
        columns = list(data.columns)
        self.data_tree['columns'] = columns
        
        # 设置列标题
        for col in columns:
            self.data_tree.heading(col, text=col)
            # 根据列类型设置列宽
            if 'date' in col.lower():
                self.data_tree.column(col, width=120)
            elif 'pct_change' in col.lower():
                self.data_tree.column(col, width=80, anchor=tk.E)
            else:
                self.data_tree.column(col, width=100, anchor=tk.E)
        
        # 添加数据
        for i, row in data.iterrows():
            values = [str(row[col]) for col in columns]
            self.data_tree.insert('', 'end', values=values)
    
    def update_overview_cards(self, correlations, analyze_north, analyze_main):
        """更新概览卡片"""
        # 更新主力资金相关性
        if 'main' in correlations:
            self.main_value.set(f"{correlations['main']:.4f}")
            # 根据相关系数设置颜色
            if correlations['main'] > 0.3:
                self.main_card.configure(style="Positive.TLabelframe")
                # 修改这里：获取实际的Label小部件并设置颜色
                for child in self.main_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['main'] < -0.3:
                self.main_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.main_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.main_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.main_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_value:
                        child.configure(foreground="#000000")
                        break
        
        # 更新北向资金相关性
        if analyze_north and 'north' in correlations:
            self.north_value.set(f"{correlations['north']:.4f}")
            if correlations['north'] > 0.3:
                self.north_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['north'] < -0.3:
                self.north_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.north_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_value:
                        child.configure(foreground="#000000")
                        break
            self.north_card.grid()  # 显示卡片
        else:
            self.north_card.grid_remove()  # 隐藏卡片
        
        # 更新资金协同性
        if analyze_north and 'fund' in correlations:
            self.fund_value.set(f"{correlations['fund']:.4f}")
            if correlations['fund'] > 0.5:
                self.fund_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['fund'] < -0.5:
                self.fund_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.fund_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#000000")
                        break
            self.fund_card.grid()  # 显示卡片
        else:
            self.fund_card.grid_remove()  # 隐藏卡片
        
        # 更新成交额相关性
        if 'turnover' in correlations:
            self.turnover_value.set(f"{correlations['turnover']:.4f}")
            if correlations['turnover'] > 0.3:
                self.turnover_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['turnover'] < -0.3:
                self.turnover_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.turnover_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#000000")
                        break
            self.turnover_card.grid()  # 显示卡片
        else:
            self.turnover_card.grid_remove()  # 隐藏卡片
    
    def update_stats(self, data, analyze_north, analyze_main):
        """更新统计信息"""
        # 更新主力资金统计
        if 'main_net_inflow' in data.columns:
            valid_main = data[data['main_net_inflow'].notna()]
            if valid_main.empty:
                self.max_inflow.set("主力资金数据不可用")
                self.max_outflow.set("--")
                self.avg_inflow.set("--")
                self.total_inflow.set("--")
            else:
                date_range = f"({valid_main['date'].iloc[0].strftime('%m-%d')}至{valid_main['date'].iloc[-1].strftime('%m-%d')})"
                self.max_inflow.set(f"最大净流入: {valid_main['main_net_inflow'].max():.2f} 亿元")
                self.max_outflow.set(f"最大净流出: {valid_main['main_net_inflow'].min():.2f} 亿元")
                self.avg_inflow.set(f"平均净流入: {valid_main['main_net_inflow'].mean():.2f} 亿元")
                self.total_inflow.set(f"总净流入: {valid_main['main_net_inflow'].sum():.2f} 亿元 {date_range}")
        
        # 更新成交额统计
        if 'total_turnover' in data.columns:
            valid_turnover = data[data['total_turnover'].notna()]
            if not valid_turnover.empty:
                self.max_turnover.set(f"最大成交额: {valid_turnover['total_turnover'].max():.2f} 亿元")
                self.min_turnover.set(f"最小成交额: {valid_turnover['total_turnover'].min():.2f} 亿元")
                self.avg_turnover.set(f"平均成交额: {valid_turnover['total_turnover'].mean():.2f} 亿元")
                self.total_turnover.set(f"总成交额: {valid_turnover['total_turnover'].sum()/10000:.2f} 万亿元")
    
    def export_data(self):
        """导出当前数据"""
        if self.current_data is None or self.current_data.empty:
            messagebox.showinfo("导出失败", "没有数据可导出")
            return
        
        try:
            from tkinter import filedialog
            file_path = filedialog.asksaveasfilename(
                defaultextension=".csv",
                filetypes=[("CSV files", "*.csv"), ("Excel files", "*.xlsx")]
            )
            
            if not file_path:
                return
                
            if file_path.endswith('.csv'):
                self.current_data.to_csv(file_path, index=False, encoding='utf-8-sig')
            elif file_path.endswith('.xlsx'):
                self.current_data.to_excel(file_path, index=False)
                
            messagebox.showinfo("导出成功", f"数据已成功导出到 {file_path}")
            self.logger.log(f"数据已导出到 {file_path}")
        except Exception as e:
            messagebox.showerror("导出失败", f"导出数据时出错: {e}")
            self.logger.log(f"导出数据失败: {e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = StockAnalysisApp(root)
    
    # 设置自定义样式
    style = ttk.Style()
    style.configure("Positive.TLabelframe.Label", foreground="#008000")
    style.configure("Negative.TLabelframe.Label", foreground="#FF0000")
    style.configure("Neutral.TLabelframe.Label", foreground="#000000")
    
    root.mainloop()



